#include "DecodeDSF.h"
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

uint8_t swap_bits[] = {
    0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 8,
    136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, 4,
    132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, 12,
    140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 2,
    130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10,
    138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6,
    134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14,
    142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1,
    129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9,
    137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5,
    133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13,
    141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 3,
    131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 11,
    139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 7,
    135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 15,
    143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255};

DecodeDSF::~DecodeDSF(void)
{
    if (file_ > 0 && file_ != fileno(stdin))
	close(file_);
}

bool DecodeDSF::open(const std::string& file_name, double seek_seconds)
{
    if (file_name == "-")
	file_ = fileno(stdin);
    else
    {
	ASSERT((file_ = ::open(file_name.c_str(), O_RDONLY)) > 0,
	    "Error opening " << file_name);
    }

    ASSERT(::read(file_, &dsd_, sizeof(dsd_)) == sizeof(dsd_), "Reading DSD header");
    ASSERT(!memcmp("DSD ", dsd_.id, 4), "Bad header");
    ASSERT(dsd_.chunk_size == 28, "Bad DSD header size");

    ASSERT(::read(file_, &fmt_, sizeof(fmt_)) == sizeof(fmt_), "Reading FMT header");
    ASSERT(!memcmp("fmt ", (char *)fmt_.id, 4), "Bad header");
    ASSERT(fmt_.chunk_size == 52, "Bad fmt length");
    ASSERT(fmt_.version == 1, "Bad version");
    ASSERT(fmt_.format_id == 0, "Bad fmt id");
    ASSERT(fmt_.channel_type == 2, "Bad channel type");
    ASSERT(fmt_.channel_count == 2, "Bad channel num");
    ASSERT(fmt_.frequency == 2822400, "Bad frequency");
    ASSERT(fmt_.bits_per_sample == 1, "Bad bps");
    ASSERT(fmt_.block_size_per_channel == 4096, "Bad block size per channel");
    ASSERT(fmt_.sample_count % 8 == 0, "Sample count does not devide by 8");

    ASSERT(::read(file_, &data_, sizeof(data_)) == sizeof(data_), "Reading data header");
    ASSERT(!memcmp("data", data_.id, 4), "Bad header");
    data_left_ = data_.chunk_size - 12;
    
    if (seek_seconds)
    {
	uint64_t offs = seek_seconds * fmt_.frequency / 8 * fmt_.channel_count;
	if (offs >= data_left_)
	    data_left_ = 0;
	else
	{
	    offs -= offs % (fmt_.channel_count * fmt_.block_size_per_channel);
	    ASSERT(lseek(file_, offs, SEEK_CUR) > 0, "Error seeking");
	    data_left_ -= offs;
	}
    }

    return true;
}

#define TMP_BUF_SIZE 20480
uint8_t tmp_buf[TMP_BUF_SIZE];
uint64_t DecodeDSF::read(uint8_t* out, uint64_t size)
{
    ASSERT(size < TMP_BUF_SIZE, "temp buf too small");
    ASSERT(size % fmt_.channel_count == 0, "Bad requested bytes");
    if (data_left_ == 0)
	return 0;
    if (size > data_left_)
	size = data_left_;
    ASSERT(::read(file_, tmp_buf, size) == (int64_t)size, "Can't read " << size << "bytes of data from file");
    data_left_ -= size;
#if 0
    for (uint64_t i = 0; i < size; ++i)
    {
	uint8_t sample = tmp_buf[(i % fmt_.channel_count) * fmt_.block_size_per_channel + (i & 0xFE)];
	out[i] = swap_bits[sample];
    }
#else
    for (uint64_t i = 0; i < size / fmt_.channel_count; ++i)
    {
	for (uint8_t ch = 0; ch < fmt_.channel_count; ++ch)
	{
	    uint8_t sample = tmp_buf[ch * fmt_.block_size_per_channel + i];
	    out[i * fmt_.channel_count + ch] = swap_bits[sample];
	}
    }
#endif

    return size;
}

bool DecodeDSF::extractID3(uint8_t* buf, int buf_size)
{
    if (!dsd_.metadata_ptr)
	return false;

    int metadata_size = dsd_.total_size - dsd_.metadata_ptr;

    ASSERT(buf_size > metadata_size, "Not enough space for ID3v2");
    ASSERT(lseek(file_, dsd_.metadata_ptr, SEEK_SET) == (int64_t)dsd_.metadata_ptr,
	"Error seeking to metadata");
    ASSERT(::read(file_, buf, metadata_size) == metadata_size, "Error reading ID3v2");
    return true;
}

